package com.whfc.mach.xxl.job;

import com.alibaba.fastjson.JSONObject;
import com.whfc.common.enums.DelFlag;
import com.whfc.common.result.PageData;
import com.whfc.common.third.cloudm.CloudMApi;
import com.whfc.common.third.cloudm.entity.*;
import com.whfc.common.util.DateUtil;
import com.whfc.common.util.MathUtil;
import com.whfc.mach.dao.AppMachDataMapper;
import com.whfc.mach.dao.AppMachDayMapper;
import com.whfc.mach.dao.AppMachMapper;
import com.whfc.mach.dao.AppMachOilRecordMapper;
import com.whfc.mach.dto.AppMachDaySegDTO;
import com.whfc.mach.entity.*;
import com.whfc.mach.enums.NetState;
import com.whfc.mach.enums.WorkState;
import com.whfc.mach.manager.MachDataLogManager;
import com.whfc.mach.manager.MachDataManager;
import com.whfc.mach.manager.util.MachUtil;
import com.whfc.mach.redis.CloudmRedisDao;
import com.whfc.mach.redis.MachDataRedisDao;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description: 云机械定时任务
 * @author: xugcheng
 * @version: 1.0
 * @date: 2022/3/9 14:11
 */
@Component
public class CloudMJob {

    private static final Logger log = LoggerFactory.getLogger(CloudMJob.class);

    private static final Double OIL_TANK_HEIGHT = 100D;

    @Autowired
    private CloudMApi cloudMApi;

    @Autowired
    private CloudmRedisDao cloudmRedisDao;

    @Autowired
    private MachDataRedisDao machDataRedisDao;

    @Autowired
    private AppMachMapper appMachMapper;

    @Autowired
    private AppMachDataMapper appMachDataMapper;

    @Autowired
    private AppMachDayMapper appMachDayMapper;

    @Autowired
    private AppMachOilRecordMapper appMachOilRecordMapper;

    @Autowired
    private MachDataLogManager machDataLogManager;

    @Autowired
    private MachDataManager machDataManager;

    /**
     * 同步云机械设备数据-实时
     */
    @XxlJob("sync-cloudm-data")
    public void syncCloudMDeviceData() {

        //获取参数
        Date now = new Date();
        Date date = DateUtil.getDate(now);
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = now;
        Date startDay = date;
        Date endDay = DateUtil.addDays(date, 1);
        String dateStr = DateUtil.formatDate(date);


        //获取token
        String token = cloudMApi.getToken();

        //获取设备列表
        List<DeviceInfo> deviceList = this.getDeviceList(token);

        log.info("云机械设备总数:{}", deviceList.size());

        for (DeviceInfo device : deviceList) {
            try {
                //查询绑定设备信息
                AppMach appMach = appMachMapper.selectMachByDeviceSn(device.getSnId());
                if (appMach != null) {
                    Integer deptId = appMach.getHoldDeptId();
                    Integer machId = appMach.getId();
                    Integer deviceId = appMach.getDeviceId();
                    String cloudmDeviceId = device.getId();
                    Double oilTankVolume = 0D;
                    Double oilTankHeight = OIL_TANK_HEIGHT;

                    //同步油箱容积
                    if (!device.getDeviceOilList().isEmpty()) {
                        oilTankVolume = device.getDeviceOilList().get(0).getDeviceFuelTankage();
                    }

                    //同步设备信息
                    DeviceInfo deviceData = cloudMApi.deviceData(token, cloudmDeviceId);
                    if (deviceData != null) {
                        NetState netState = parseNetState(deviceData.getWorkStatus());
                        WorkState workState = parseWorkState(deviceData.getWorkStatus());
                        AppMachData machData = new AppMachData();
                        machData.setMachId(machId);
                        machData.setTime(deviceData.getCollectTime());
                        machData.setLng(deviceData.getLng());
                        machData.setLat(deviceData.getLat());
                        machData.setNetState(netState.getValue());
                        machData.setWorkState(workState.getValue());
                        machData.setOilPos(deviceData.getOilLevel() * 10);
                        appMachDataMapper.insertOrUpdate(machData);

                        appMach.setMachCode(device.getDeviceName());
                        appMach.setMachModelName(device.getBrand() + device.getModel());
                        appMach.setOilTankVolume(oilTankVolume);
                        appMach.setOilTankHeight(oilTankHeight);
                        appMach.setDeliveryTime(device.getFactoryTime());
                        appMach.setTotalWorkHours(device.getWorkTime());
                        appMach.setMachName(device.getId());
                        appMachMapper.updateByPrimaryKey(appMach);
                    }

                    //同步设备工时详情
                    List<WorkTime> workTimeList = cloudMApi.workTimeDetailStatus(token, cloudmDeviceId, date);
                    if (!workTimeList.isEmpty()) {
                        List<AppMachDaySegDTO> segDTOList = workTimeList.stream().map(workTime -> translate(workTime, dateStr)).collect(Collectors.toList());
                        machDataManager.saveMachSeg(machId, date, segDTOList);
                    }

                    //同步设备工时统计
                    List<WorkTime> workTimeDays = cloudMApi.workTimeList(token, cloudmDeviceId, startDay, endDay);
                    if (!workTimeDays.isEmpty()) {
                        for (WorkTime workTime : workTimeDays) {
                            AppMachDay machDay = this.translate(workTime, machId, deptId);
                            appMachDayMapper.insertUpdate(machDay);
                        }
                    }

                    //同步设备定位
                    List<LocInfo> locList = cloudMApi.location(token, cloudmDeviceId, startDay, endDay);
                    if (!locList.isEmpty()) {
                        Date locTime = getLocTime(cloudmDeviceId, date);
                        List<AppMachDataLog> logList = locList.stream().filter(loc -> loc.getTime().after(locTime)).map(loc -> translate(loc, machId, deviceId)).collect(Collectors.toList());
                        if (logList.size() > 0) {
                            machDataLogManager.addMachDataLog(logList);
                        }
                        setLocTime(cloudmDeviceId, locList.get(locList.size() - 1).getTime());
                        machDataRedisDao.delMachData(machId, date);
                    }

                    //同步设备油位
                    Date oilLevelTime = this.getOilLevelTime(cloudmDeviceId, date);
                    List<OilInfo> oilList = cloudMApi.oilLevel(token, cloudmDeviceId, startTime, endTime);
                    if (!oilList.isEmpty()) {
                        List<AppMachDataLog> logList = oilList.stream().filter(oil -> oil.getTime().after(oilLevelTime)).map(oil -> translate(oil, machId, deviceId)).collect(Collectors.toList());
                        if (logList.size() > 0) {
                            machDataLogManager.addMachDataLog(logList);
                        }
                        setOilLevelTime(cloudmDeviceId, oilList.get(oilList.size() - 1).getTime());
                        machDataRedisDao.delMachData(machId, date);
                    }

                    //同步设备加油记录
                    Double oilAdd = 0D;
                    OilAddData oilAddData = cloudMApi.oilAddData(token, cloudmDeviceId, startTime, endTime);
                    if (oilAddData != null && oilAddData.getAddOilCount() > 0) {
                        Date oilAddTime = this.getOilAddTime(cloudmDeviceId, date);
                        List<OilAddPoint> pointList = oilAddData.getAddOilPointDTOList();
                        List<AppMachOilRecord> oilRecordList = pointList.stream().filter(oil -> oil.getAddOilTime().after(oilAddTime)).map(oil -> translate(oil, machId)).collect(Collectors.toList());
                        if (oilRecordList.size() > 0) {
                            appMachOilRecordMapper.batchInserts(oilRecordList);
                        }
                        setOilAddTime(cloudmDeviceId, pointList.get(pointList.size() - 1).getAddOilTime());
                        oilAdd = oilAddData.getAddOilTotal();
                    }


                    //每日油量统计
                    if (!oilList.isEmpty() && oilAddData != null) {
                        Double oilWear = this.oilWear(oilList, oilTankVolume, oilTankHeight, oilAdd);
                        log.info("云机械设备用油量计算,machId:{},date:{},oilWear:{},oilAdd:{}", machId, dateStr, oilWear, oilAdd);
                        AppMachDay machDay = new AppMachDay();
                        machDay.setHoldDeptId(deptId);
                        machDay.setMachId(machId);
                        machDay.setDate(date);
                        machDay.setOilAdd(oilAdd);
                        machDay.setOilWear(oilWear);
                        appMachDayMapper.insertUpdate(machDay);
                    }
                }
            } catch (Exception ex) {
                log.warn("云机械设备数据同步错误", ex);
            }
        }
    }

    /**
     * 同步云机械设备数据-历史
     */
    @XxlJob("sync-cloudm-data-history")
    public void syncCloudMDeviceDataHistory() {

        //获取参数
        String paramStr = XxlJobHelper.getJobParam();
        if (StringUtils.isEmpty(paramStr)) {
            XxlJobHelper.handleFail("未配置参数");
            return;
        }
        JSONObject params = JSONObject.parseObject(paramStr);
        Date date = params.getDate("date");
        Date startTime = DateUtil.getDateBegin(date);
        Date endTime = DateUtil.getDateEnd(date);
        Date startDay = date;
        Date endDay = DateUtil.addDays(date, 1);
        String dateStr = DateUtil.formatDate(date);

        //获取token
        String token = cloudMApi.getToken();

        //获取设备列表
        List<DeviceInfo> deviceList = this.getDeviceList(token);

        //数据同步
        for (DeviceInfo device : deviceList) {
            try {
                //查询绑定设备信息
                AppMach appMach = appMachMapper.selectMachByDeviceSn(device.getSnId());
                if (appMach != null) {
                    Integer deptId = appMach.getHoldDeptId();
                    Integer machId = appMach.getId();
                    Integer deviceId = appMach.getDeviceId();
                    String cloudmDeviceId = device.getId();
                    Double oilTankVolume = appMach.getOilTankVolume();
                    Double oilTankHeight = appMach.getOilTankHeight();

                    //同步设备工时详情
                    List<WorkTime> workTimeList = cloudMApi.workTimeDetailStatus(token, cloudmDeviceId, date);
                    if (!workTimeList.isEmpty()) {
                        List<AppMachDaySegDTO> segDTOList = workTimeList.stream().map(workTime -> translate(workTime, dateStr)).collect(Collectors.toList());
                        machDataManager.saveMachSeg(machId, date, segDTOList);
                    }

                    //同步设备工时统计
                    List<WorkTime> workTimeDays = cloudMApi.workTimeList(token, cloudmDeviceId, startDay, endDay);
                    if (!workTimeDays.isEmpty()) {
                        for (WorkTime workTime : workTimeDays) {
                            AppMachDay machDay = this.translate(workTime, machId, deptId);
                            appMachDayMapper.insertUpdate(machDay);
                        }
                    }

                    //同步设备定位
                    List<LocInfo> locList = cloudMApi.location(token, cloudmDeviceId, startDay, endDay);
                    if (!locList.isEmpty()) {
                        List<AppMachDataLog> logList = locList.stream().map(loc -> translate(loc, machId, deviceId)).collect(Collectors.toList());
                        machDataLogManager.addMachDataLog(logList);
                        machDataRedisDao.delMachData(machId, date);
                    }

                    //同步设备油位
                    List<OilInfo> oilList = cloudMApi.oilLevel(token, cloudmDeviceId, startTime, endTime);
                    if (!oilList.isEmpty()) {
                        List<AppMachDataLog> logList = oilList.stream().map(oil -> translate(oil, machId, deviceId)).collect(Collectors.toList());
                        machDataLogManager.addMachDataLog(logList);
                        machDataRedisDao.delMachData(machId, date);
                    }

                    //同步设备加油记录
                    Double oilAdd = 0D;
                    OilAddData oilAddData = cloudMApi.oilAddData(token, cloudmDeviceId, startTime, endTime);
                    if (oilAddData != null && oilAddData.getAddOilCount() > 0) {
                        List<OilAddPoint> pointList = oilAddData.getAddOilPointDTOList();
                        List<AppMachOilRecord> oilRecordList = pointList.stream().map(oil -> translate(oil, machId)).collect(Collectors.toList());
                        appMachOilRecordMapper.deleteLogicByMachId(machId, startTime, endTime);
                        appMachOilRecordMapper.batchInserts(oilRecordList);
                        oilAdd = oilAddData.getAddOilTotal();
                    }

                    //每日油量统计
                    if (!oilList.isEmpty() && oilAddData != null) {
                        Double oilWear = this.oilWear(oilList, oilTankVolume, oilTankHeight, oilAdd);
                        log.info("云机械设备用油量计算,machId:{},date:{},oilWear:{},oilAdd:{}", machId, dateStr, oilWear, oilAdd);
                        AppMachDay machDay = new AppMachDay();
                        machDay.setHoldDeptId(deptId);
                        machDay.setMachId(machId);
                        machDay.setDate(date);
                        machDay.setOilAdd(oilAdd);
                        machDay.setOilWear(oilWear);
                        appMachDayMapper.insertUpdate(machDay);
                    }
                }
            } catch (Exception ex) {
                log.warn("云机械设备数据同步错误", ex);
            }
        }
    }

    /**
     * 获取设备列表
     *
     * @param token
     * @return
     */
    private List<DeviceInfo> getDeviceList(String token) {
        int pageNum = 1;
        int pageSize = 100;
        int pages = 0;
        List<DeviceInfo> deviceList = null;
        do {
            PageData<DeviceInfo> pageData = cloudMApi.deviceList(token, pageNum, pageSize);
            log.info("云机械设备列表,total:{},pages:{},pageNum:{},pageSize:{}", pageData.getTotal(), pageData.getPages(), pageData.getPageNum(), pageData.getPageSize());
            if (deviceList == null) {
                deviceList = new ArrayList<>(MathUtil.long2int(pageData.getTotal()));
            }
            deviceList.addAll(pageData.getList());
            pages = pageData.getPages();
            pageNum++;
        } while (pageNum <= pages);
        return deviceList;
    }

    /**
     * 解析网络状态
     *
     * @param cloudmState 工作状态 -1:离线 0:在线 1:怠速 2:负荷
     * @return
     */
    private NetState parseNetState(Integer cloudmState) {
        return cloudmState != null && cloudmState >= 0 ? NetState.ONLINE : NetState.OFFLINE;
    }

    /**
     * 解析工作状态
     *
     * @param cloudmState
     * @return
     */
    private WorkState parseWorkState(Integer cloudmState) {
        if (cloudmState != null) {
            if (cloudmState.equals(-1)) {
                return WorkState.OFFLINE;
            } else if (cloudmState.equals(0)) {
                return WorkState.STOP;
            } else if (cloudmState.equals(1)) {
                return WorkState.IDLE;
            } else if (cloudmState.equals(2)) {
                return WorkState.RUN;
            }
        }
        return WorkState.OFFLINE;
    }

    /**
     * 获取同步定位时间
     *
     * @param cloudmDeviceId
     * @param today
     * @return
     */
    private Date getLocTime(String cloudmDeviceId, Date today) {
        Date time = cloudmRedisDao.getDeviceLocTime(cloudmDeviceId);
        if (time == null) {
            return today;
        }
        return time;
    }

    /**
     * 设置同步定位时间
     *
     * @param cloudmDeviceId
     * @param time
     */
    private void setLocTime(String cloudmDeviceId, Date time) {
        cloudmRedisDao.setDeviceLocTime(cloudmDeviceId, time);
    }

    /**
     * 获取同步油位时间
     *
     * @param cloudmDeviceId
     * @param today
     * @return
     */
    private Date getOilLevelTime(String cloudmDeviceId, Date today) {
        Date time = cloudmRedisDao.getDeviceOilLevelTime(cloudmDeviceId);
        if (time == null) {
            return today;
        }
        return time;
    }

    /**
     * 设置油位同步时间
     *
     * @param cloudmDeviceId
     * @param time
     */
    private void setOilLevelTime(String cloudmDeviceId, Date time) {
        cloudmRedisDao.setDeviceOilLevelTime(cloudmDeviceId, time);
    }

    /**
     * 获取加油记录同步时间
     *
     * @param cloudmDeviceId
     * @param today
     * @return
     */
    private Date getOilAddTime(String cloudmDeviceId, Date today) {
        Date time = cloudmRedisDao.getDeviceOilAddTime(cloudmDeviceId);
        if (time == null) {
            return today;
        }
        return time;
    }

    /**
     * 设置加油记录同步时间
     *
     * @param cloudmDeviceId
     * @param time
     */
    private void setOilAddTime(String cloudmDeviceId, Date time) {
        cloudmRedisDao.setDeviceOilAddTime(cloudmDeviceId, time);
    }

    /**
     * 定位信息转换
     *
     * @param locInfo
     * @param machId
     * @param deviceId
     * @return
     */
    private AppMachDataLog translate(LocInfo locInfo, Integer machId, Integer deviceId) {
        AppMachDataLog log = new AppMachDataLog();
        log.setMachId(machId);
        log.setDeviceId(deviceId);
        log.setTime(locInfo.getTime());
        log.setLng(locInfo.getLng());
        log.setLat(locInfo.getLat());
        log.setDelFlag(DelFlag.UNDELETE.getValue());
        return log;
    }

    /**
     * 油位信息转换
     *
     * @param oilInfo
     * @param machId
     * @param deviceId
     * @return
     */
    private AppMachDataLog translate(OilInfo oilInfo, Integer machId, Integer deviceId) {
        AppMachDataLog log = new AppMachDataLog();
        log.setMachId(machId);
        log.setDeviceId(deviceId);
        log.setTime(oilInfo.getTime());
        log.setOilPos(oilInfo.getOilLevel() * 10);
        log.setDelFlag(DelFlag.UNDELETE.getValue());
        return log;
    }

    /**
     * 工时分段信息转换
     *
     * @param workTime
     * @param today
     * @return
     */
    private AppMachDaySegDTO translate(WorkTime workTime, String today) {
        AppMachDaySegDTO segDTO = new AppMachDaySegDTO();
        segDTO.setWorkState(parseWorkState(workTime.getStatus()).getValue());
        segDTO.setStartTime(DateUtil.parseDateTime(String.format("%s %s", today, workTime.getStartTime())));
        segDTO.setEndTime(DateUtil.parseDateTime(String.format("%s %s", today, workTime.getEndTime())));
        int times = MachUtil.calculateTime(segDTO.getStartTime(), segDTO.getEndTime());
        segDTO.setTimes(times);
        return segDTO;
    }

    /**
     * 转换每日工时统计
     *
     * @param workTime
     * @param machId
     * @param deptId
     * @return
     */
    private AppMachDay translate(WorkTime workTime, Integer machId, Integer deptId) {
        AppMachDay record = new AppMachDay();
        record.setHoldDeptId(deptId);
        record.setMachId(machId);
        record.setDate(workTime.getDate());
        record.setWorkTimes(hoursToSeconds(workTime.getWorkTime()));
        record.setIdleTimes(hoursToSeconds(workTime.getIdleTime()));
        return record;
    }

    /**
     * 加油记录转换
     *
     * @param point
     * @return
     */
    private AppMachOilRecord translate(OilAddPoint point, Integer machId) {
        AppMachOilRecord record = new AppMachOilRecord();
        record.setMachId(machId);
        record.setTime(point.getAddOilTime());
        record.setOilAddHardware(point.getOilCharge());
        record.setOilAdd(point.getOilCharge());
        return record;
    }

    /**
     * 小时转换秒
     *
     * @param hours
     * @return
     */
    private Integer hoursToSeconds(Double hours) {
        return MathUtil.doule2int(hours * 3600);
    }

    /**
     * 计算油耗
     *
     * @param oilList
     * @param oilTankVolume
     * @param oilTankHeight
     * @param oilAdd
     * @return
     */
    private Double oilWear(List<OilInfo> oilList, Double oilTankVolume, Double oilTankHeight, Double oilAdd) {
        OilInfo begin = oilList.get(oilList.size() - 1);
        OilInfo end = oilList.get(0);
        if (begin.getTime().after(end.getTime())) {
            begin = oilList.get(0);
            end = oilList.get(oilList.size() - 1);
        }
        Integer levelDiff = begin.getOilLevel() - end.getOilLevel();
        Double oilDiff = MathUtil.round(levelDiff * oilTankVolume / oilTankHeight, 2);
        Double oilWear = oilDiff + oilAdd;
        oilWear = oilWear > 0 ? oilWear : 0;
        return oilWear;
    }
}
